#============================================================
#    Callable Util
#============================================================
# - datetime: 2022-12-17 19:10:36
#============================================================
## 回调工具
##
##节点在 enter_tree 之后进行获取设置这个节点
class_name CallableUtil


## 没别的，仅仅调用一下这个回调
static func execute(callable: Callable):
	return callable.call()


## 节点在场景中时信号才连接调用一次这个 [Callable]，如果节点已经在场景中，则直接调用 [Callable] 方法
static func call_callable_once_in_tree(signal_or_node, callable: Callable, params: Array = []):
	if signal_or_node is Node:
		signal_or_node = signal_or_node.tree_entered
	else:
		assert(signal_or_node is Signal, "signal_or_node 参数必须是 Node 或 Signal 类型")
	var node = signal_or_node.get_object()
	if not node.is_inside_tree():
		signal_or_node.connect(func():
			callable.callv(params)
		, Object.CONNECT_ONE_SHOT)
	else:
		callable.callv(params)


## 节点初始化后设置属性
##[br]
##[br][code]node[/code]  设置属性的节点
##[br][code]property_to_node_path_map[/code]  属性对应的要获取的节点的路径。key为属性，
##value 为节点路径
##[br][code]get_path_to_node[/code]  根据这个节点获取这个路径节点，如果为 null，则默认为
##node 参数值
##[br][code]set_node_callable[/code]  如何获取设置节点的方法，这个方法需要有两个参数，第一个参数为
##[String] 类型接收属性名，第二个为 [NodePath] 类型，用于接收节点路径，返回一个 [Node] 类型的数据
static func set_node_propertys_by_path_map(
	node: Node, 
	property_to_node_path_map: Dictionary, 
	get_path_to_node: Node = null
) -> void:
	if get_path_to_node == null:
		get_path_to_node = node
	
	# 获取设置节点的方法
	var set_node_callable = func(property: String, node_path: NodePath):
		if node[property] == null:
			node.set(property, get_path_to_node.get_node_or_null(node_path))
	
	call_callable_once_in_tree(node.tree_entered, func():
		var node_path : NodePath
		for prop in property_to_node_path_map:
			node_path = property_to_node_path_map[prop]
			# 获取节点设置属性
			set_node_callable.call(prop, node_path)
	)


##  根据节点名设置属性
##[br]
##[br][code]node[/code]  设置属性的节点
##[br][code]node_path_list[/code]  节点路径列表，如果有这个节点名称的属性，则进行设置
##[br][code]get_path_to_node[/code]  根据这个节点获取这个路径的节点，如果为 null，则默认为
##node 参数值
static func set_node_propertys_by_node_name(
	node: Node, 
	node_path_list: PackedStringArray, 
	get_path_to_node: Node = null
):
	call_callable_once_in_tree(node.tree_entered, func():
		var prop_to_node_path_map := {}
		var prop : String
		for node_path in node_path_list:
			prop = str(node_path).get_file().replace("%", "")
			if prop in node:
				prop_to_node_path_map[prop] = node_path
			else:
				printerr(node, " 节点中没有这个属性：", prop)
		set_node_propertys_by_path_map(node, prop_to_node_path_map, get_path_to_node)
	)


##  设置带有前缀的属性的唯一名节点。（唯一节点名为这个属性去掉前缀后的名称）
##[br]
##[br][code]node[/code]  设置属性的节点
##[br][code]prefix[/code]  属性前缀
static func set_unique_node_propertys_by_prefix(
	node: Node,
	prefix: String, 
	get_path_to_node: Node = null
):
	call_callable_once_in_tree(node.tree_entered, func():
		# 获取这个前缀的属性名
		var property_list = (node.get_property_list()
			.filter(func(data): return data['usage'] == PROPERTY_USAGE_SCRIPT_VARIABLE and data['name'].begins_with(prefix))
			.map(func(data): return data['name'] )
		)
		var prop_to_node_path_map = {}
		for prop in property_list:
			prop_to_node_path_map[prop] = "%" + prop.trim_prefix(prefix)
		if get_path_to_node == null:
			get_path_to_node = node
		var node_path
		for property in prop_to_node_path_map:
			node_path = prop_to_node_path_map[property]
			node[property] = get_path_to_node.get_node(node_path)
	)


##  根据前缀 find_node 设置节点属性
##[br]
##[br][code]node[/code]  设置属性的节点
##[br][code]prefix[/code]  属性前缀
##[br][code]get_path_to_node[/code]  以节点获取节点路径，传入 [Node] 类型或是一个无参的返回类型为
##[Node] 类型的 [Callable]
static func set_node_propertys_by_prefix_find_node(
	node: Node,
	prefix: String, 
	get_path_to_node = null
):
	call_callable_once_in_tree(node.tree_entered, func():
		# 获取这个前缀的属性名
		var property_list = (node.get_property_list()
			.filter(func(data): return data['usage'] == PROPERTY_USAGE_SCRIPT_VARIABLE and data['name'].begins_with(prefix))
			.map(func(data): return data['name'] )
		)
		var prop_to_node_path_map = {}
		for prop in property_list:
			prop_to_node_path_map[prop] = prop.trim_prefix(prefix)
		if get_path_to_node is Callable:
			get_path_to_node = get_path_to_node.call()
		get_path_to_node = get_path_to_node as Node
		
		# 设置属性
		var node_path
		for property in prop_to_node_path_map:
			node_path = prop_to_node_path_map[property]
			node.set(property, get_path_to_node.get_node(node_path))
	)


##  等待一帧调用这个方法
##[br]
##[br][code]callable[/code]  调用的方法
##[br][code]process_callback[/code]  线程类型，默认为 [member Timer.TIMER_PROCESS_IDLE]
static func await_process_once(callable: Callable, process_callback := Timer.TIMER_PROCESS_IDLE):
	if process_callback == Timer.TIMER_PROCESS_IDLE:
		Engine.get_main_loop().process_frame.connect( callable, Object.CONNECT_ONE_SHOT )
	else:
		Engine.get_main_loop().physics_frame.connect( callable, Object.CONNECT_ONE_SHOT )
	
